package main

import (
	"crypto/tls"
	"crypto/x509"
	_ "embed"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/credentials/insecure"
	. "monitor-agent/configs"
	"monitor-agent/internal/fetch"
	. "monitor-agent/internal/report"
	"monitor-agent/internal/utils"
	. "monitor-agent/pkg/logging"
	"monitor-agent/registry"
	"net"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"
)

// agent tls certification
var (
	//go:embed cert/client.pem
	certFileByte []byte

	//go:embed cert/client.key
	keyFileByte []byte

	//go:embed cert/ca.pem
	caFileByte []byte
)

func main() {
	// init logger
	logger := Logger().Named("boot")
	//goland:noinspection GoUnhandledErrorResult
	defer logger.Sync()

	// init setting from config file
	err := LoadToml()
	if err != nil {
		logger.Fatal("failed to load config", zap.Error(err))
	}

	// init log file
	{
		if err := os.MkdirAll(Config.Env.Log, 0775); err != nil {
			logger.Fatal("init log dir failed", zap.Error(err))
		}
		errorFile := filepath.Join(Config.Env.Log, "error.log")
		debugFile := filepath.Join(Config.Env.Log, "debug.log")
		if err := SetLogFile(errorFile, debugFile); err != nil {
			logger.Fatal("init log file failed", zap.Error(err))
		}
	}

	var conn *grpc.ClientConn
	var option credentials.TransportCredentials
	//init report connection with encryption
	if Config.TLS.SafeConnection {
		cert, err := tls.X509KeyPair(certFileByte, keyFileByte)
		if err != nil {
			logger.Fatal("failed to open certification file", zap.Error(err))
		}
		certPool := x509.NewCertPool()
		ok := certPool.AppendCertsFromPEM(caFileByte)
		if !ok {
			logger.Fatal("failed to append certification into pool")
		}

		option = credentials.NewTLS(&tls.Config{
			Certificates: []tls.Certificate{cert},
			ServerName:   Config.TLS.ServerName,
			RootCAs:      certPool,
		})
	} else {
		option = insecure.NewCredentials()
	}

	conn, err = grpc.Dial(Config.GRPC.BackendAddr, grpc.WithTransportCredentials(option))
	if err != nil {
		logger.Fatal("failed to create connection", zap.Error(err))
	}
	defer func() {
		_ = conn.Close()
	}()

	// init monitor client
	rClient := NewReportServerClient(conn)

	// start report procedure
	if err := registry.InitController(fetcher, rClient,
		!utils.IsPublicIP(net.ParseIP(Config.GRPC.BackendAddr))); err != nil {
		logger.Fatal("failed to init controller", zap.Error(err))
	}

	// Wait for exit and gracefully shutdown
	{
		osSignals := make(chan os.Signal, 1)
		signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM)
		<-osSignals

		logger.Info("gracefully shutdown monitor-agent service successfully")
		close(osSignals)
	}
}

func fetcher() {
	fetch.Fetch(registry.GetReportClient(), registry.GetIPStatus())
}
